﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PSL.ST
{
    /// <summary>
    /// Base of every treenode
    /// </summary>
    public abstract class TreeNode
    {
        #region Private Member
        private TreeNode parentNode;
        private IList<TreeNode> children;
        private TreeNodeType nodeType;
        #endregion

        #region Constructor
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="ParentNode">Parent-Node</param>
        /// <param name="NodeType">TreeNode-Type</param>
        public TreeNode(TreeNode ParentNode, TreeNodeType NodeType)
        {
            children = new List<TreeNode>();
            parentNode = ParentNode;
            nodeType = NodeType;
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Create new child-node
        /// </summary>
        /// <typeparam name="T">Generic Node-Type</typeparam>
        /// <returns>Instance of an TreeNode</returns>
        public T CreateChildNode<T>() where T : TreeNode
        {
            T returnValue = (T)Activator.CreateInstance(typeof(T), this);

            // Add the new node to the current child-list
            this.Children.Add(returnValue);

            return returnValue;
        }

        /// <summary>
        /// Get all children of a specific type
        /// </summary>
        /// <typeparam name="T">Type</typeparam>
        /// <returns>List with all children</returns>
        public IList<T> FindChildrenOfType<T>()
        {
            return children.Where(Item => Item is T).OfType<T>().ToList();
        }

        /// <summary>
        /// Get all childres which are NOT of a specifig type
        /// </summary>
        /// <typeparam name="T">Type</typeparam>
        /// <returns>List with all children</returns>
        public IList<TreeNode> FindChildrenBesidesOfType<T>()
        {
            return children.Where(Item => (Item is T) == false).ToList();
        }

        /// <summary>
        /// Get al children of specific TreeNodeType
        /// </summary>
        /// <param name="NodeType">Node-Type</param>
        /// <returns>List with all children</returns>
        public IList<TreeNode> FindChildrenOfType(TreeNodeType NodeType)
        {
            return children.Where(Item => Item.NodeType == NodeType).ToList();
        }

        /// <summary>
        /// Get a copy of the current tree, Node
        /// </summary>
        /// <param name="IsDeepCopy">If the copy is a deep-copy</param>
        /// <returns></returns>
        public TreeNode GetCopy(bool IsDeepCopy)
        {
            // Get a close of the current node
            TreeNode clone = CloneNode();

            if (IsDeepCopy)
            {
                foreach (var child in Children)
                {
                    var childClone = child.GetCopy(IsDeepCopy);

                    clone.Children.Add(childClone);
                    childClone.ParentNode = clone;
                }
            }

            return clone;
        }

        /// <summary>
        /// Return a clone of the current TreeNode
        /// </summary>
        /// <returns>Instance of the current tree-node</returns>
        public abstract TreeNode CloneNode();

        /// <summary>
        /// Execute all tasks of the current tree-node
        /// </summary>
        /// <param name="Engine">Engine in which the TreeNode will be executed</param>
        /// <returns>Return a value of the treenode produce a value</returns>
        public abstract dynamic Execute(PSLEngine Engine);
        #endregion

        #region Public Member
        /// <summary>
        /// Contains all syntax children of this node
        /// </summary>
        internal IList<TreeNode> Children
        {
            get { return children; }
        }

        /// <summary>
        /// Parent of this Node
        /// </summary>
        public TreeNode ParentNode
        {
            get { return parentNode; }
            set { parentNode = value;}
        }

        /// <summary>
        /// Type of the current tree-node
        /// </summary>
        public TreeNodeType NodeType
        {
            get { return nodeType; }
        }
        #endregion
    }
}
